summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAnton Luka Šijanec <anton@sijanec.eu>2022-02-25 00:54:36 +0100
committerAnton Luka Šijanec <anton@sijanec.eu>2022-02-25 00:54:36 +0100
commitdc07e4bf7f4628852eee26b114348498caad0182 (patch)
tree597cce2a6e81a13a959b1beae2508b761ef04773
parenti suppose parsing json is easyer with cjson than lejp (: (diff)
downloaddiscord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar.gz
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar.bz2
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar.lz
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar.xz
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.tar.zst
discord.c-dc07e4bf7f4628852eee26b114348498caad0182.zip
-rw-r--r--.gitignore4
-rw-r--r--Makefile2
-rw-r--r--README.md1
-rw-r--r--src/api.c152
-rw-r--r--src/h.c189
-rw-r--r--src/json.c1
-rw-r--r--src/main.c12
7 files changed, 231 insertions, 130 deletions
diff --git a/.gitignore b/.gitignore
index 9b05aed..06a0a6d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,8 +4,10 @@ a.out
# debugging log files
valgrind-out.txt
.gdb_history
-out.txt
+out*.txt
core
+gmon.out
+analysis.txt
# files I like to keep in my WD
src.old/
src/ui.glade~
diff --git a/Makefile b/Makefile
index 19d2c60..a10bd45 100644
--- a/Makefile
+++ b/Makefile
@@ -2,11 +2,11 @@ DESTDIR=/
SRCFILE=src/main.c
BINFILE=discord.c
O=0
+CC=cc
CFLAGS += -Wextra -Wall -pedantic -g -O$O -Itmp -Isrc -I. -odiscord.c -Wno-unused-parameter -rdynamic -finput-charset=UTF-8 -fextended-identifiers
LIBS += -lm
CFLAGS += $(shell pkg-config --cflags libwebsockets) $(shell pkg-config --cflags gtk+-3.0) $(shell pkg-config --cflags gmodule-export-2.0)
LIBS += $(shell pkg-config --libs libwebsockets) $(shell pkg-config --libs gtk+-3.0) $(shell pkg-config --libs gmodule-export-2.0) -lcjson
-CC=cc
VGARGS += --leak-check=full --track-origins=yes --verbose --log-file=valgrind-out.txt --suppressions=/usr/share/glib-2.0/valgrind/glib.supp --suppressions=tmp/gtk.supp --suppressions=/usr/lib/i386-linux-gnu/valgrind/default.supp --suppressions=tmp/gnome.supp --show-leak-kinds=definite --suppressions=misc/misc.supp --suppressions=/usr/share/gtk-3.0/valgrind/gtk.supp --leak-resolution=low
# notparallel ker morajo biti ukazi izvedeni po vrsti, mkdir se mora zgoditi pred xxd recimo.
diff --git a/README.md b/README.md
index 91d681e..3d4d023 100644
--- a/README.md
+++ b/README.md
@@ -93,3 +93,4 @@ it would be useful to have an android port, and luckily this is possible with li
* `make -e CC=gcc` to choose a compiler instead of `cc`.
* `make -e CC="clang -fsanitize=address" && ASAN_OPTIONS=detect_leaks=1` to use `clang` leak checker
* `scan-build make` to staticly analyze build with `clang` (`clang-tools` package required). `gcc`'s `-fanalyze` can't be used without many false positives, because the code is not written so pedantically (without `__attribute__`s on function arguments, return values, ...).
+* `make -e CC="cc -pg"` can be used to generate a gprof binary (gcc/clang only, tcc has no support). After running the produced executable, run `gprof discord.c gmon.out > analysis.txt` for report.
diff --git a/src/api.c b/src/api.c
index fbd9495..32a2058 100644
--- a/src/api.c
+++ b/src/api.c
@@ -24,6 +24,17 @@
cJSON_GetObjectItem(cJSON_GetObjectItem3(ro, na1, na2, na3), na4) : NULL)
#define cJSON_GetObjectItem5(r, n1, n2, n3, n4, n5) (cJSON_GetObjectItem4(r, n1, n2, n3, n4) ? \
cJSON_GetObjectItem(cJSON_GetObjectItem4(r, n1, n2, n3, n4), n5) : NULL)
+#define cJSON_GSV cJSON_GetStringValue
+#define cJSON_GNV cJSON_GetNumberValue
+#define cJSON_GAS cJSON_GetArraySize
+#define cJSON_GOI cJSON_GetObjectItem
+#define cJSON_GOI2 cJSON_GetObjectItem2
+#define cJSON_GOI3 cJSON_GetObjectItem3
+#define cJSON_GOI4 cJSON_GetObjectItem4
+#define cJSON_GOI5 cJSON_GetObjectItem5
+#define cJSON_GSV cJSON_GetStringValue
+#define cJSON_IN cJSON_IsNumber
+#define cJSON_AFE cJSON_ArrayForEach
unsigned char dc_api_identify_u[] = {
#include <identify.xxd>
};
@@ -37,19 +48,26 @@ void dc_api_stack (struct dc_api_io i) { /* stack output struct to be delivered
}
unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_channel * c) {
unsigned long long int p = 0; /* note: this is NOT according to server's implementation of */
- struct dc_role ** role = &c->guild->role; /* permission parsing, but should suffice for most */
- while (*role) { /* cases. */
- if ((*role)->status & DC_EVERYONE || dc_find_user((*role)->users, (*role)->users_length, u->id))
+ struct dc_role ** role = &c->guild->role; /* perm parsing, but should suffice 4 most cases */
+ if (!*role) /* see struct dc_guild: if NULL then assume all permissions - a DM guild */
+ return DC_ALL_PERMISSIONS;
+ while (*role) {
+ if (DC_ROLE_EVERYONE(*role)
+ || dc_find_user((*role)->users, (*role)->users_length, u->id))
p |= (*role)->permissions;
role = &(*role)->next;
}
if (p & DC_ADMIN)
return DC_ALL_PERMISSIONS;
for (size_t i = 0; i < c->permissions_length; i++)
- if (c->permissions[i]->user == u || dc_find_user(c->permissions[i]->role->users, c->permissions[i]->role->users_length, u->id) || c->permissions[i]->role->status & DC_EVERYONE) {
+ if (c->permissions[i]->user
+ ? c->permissions[i]->user == u
+ : dc_find_user(c->permissions[i]->role->users,
+ c->permissions[i]->role->users_length, u->id)
+ || DC_ROLE_EVERYONE(c->permissions[i]->role)) {
p &= ~c->permissions[i]->deny;
p |= c->permissions[i]->allow;
- if ((*role)->permissions & DC_ADMIN)
+ if (p & DC_ADMIN)
return DC_ALL_PERMISSIONS;
}
return p;
@@ -57,15 +75,17 @@ unsigned long long int dc_calculate_permissions (struct dc_user * u, struct dc_c
struct dc_user * dc_parse_user (struct dc_user * dst, const cJSON * src) {
char * cp;
if (!dst)
- dst = dc_user_init();
- if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "username"))))
+ dst = dc_user_init();
+ if ((cp = cJSON_GSV(cJSON_GOI(src, "username"))))
dst->username = strdup(cp);
- if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "discriminator"))))
+ if ((cp = cJSON_GSV(cJSON_GOI(src, "discriminator"))))
dst->discriminator = atoi(cp);
- else
- dst->discriminator = -1;
- if ((cp = cJSON_GetStringValue(cJSON_GetObjectItem(src, "id"))))
+ if ((cp = cJSON_GSV(cJSON_GOI(src, "id"))))
dst->id = strtoull(cp, NULL, 10);
+ if (!dst->username || dst->discriminator == -1) { /* it's quite useless to store only ids */
+ dc_user_free(dst, DC_UNSET);
+ return NULL;
+ }
return dst;
}
static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us, void * in, size_t len) {
@@ -207,60 +227,100 @@ static int dc_lws_cb (struct lws * wsi, enum lws_callback_reasons rs, void * us,
fprintf(stdout, "%.*s", len, (const unsigned char *) in);
char * serialized = dc_json(pass->json, in, len);
while (serialized) {
- cJSON * json = cJSON_Parse(serialized);
- cJSON * obj;
+ cJSON * obj, * obje, * json = cJSON_Parse(serialized);
char * st;
if (getenv("DC_J")) {
st = cJSON_Print(json);
- if (st) {
- printf("%s\n", st);
- cJSON_free(st);
- } else
- fprintf(stderr, "_RECEIVE: _Print NULL: %s\n", serialized);
+ printf("%s\n", st);
+ cJSON_free(st);
+ }
+#define DC_PTYP(t) ((st = cJSON_GSV(cJSON_GOI(json, "t"))) && !strcmp(st, t))
+ if (DC_PTYP("READY") && getenv("DC_R")) {
+ printf("%s\n", (st = cJSON_Print(json)));
+ cJSON_free(st);
}
- pass->api_io.client->last_packet
- = cJSON_GetNumberValue(cJSON_GetObjectItem(json, "s"));
- if (cJSON_GetNumberValue(cJSON_GetObjectItem(json, "op"))
- == DC_PING) {
+ if ((st = cJSON_GSV(cJSON_GOI(json, "t"))))
+ fprintf(stderr, "t: %s\n", st);
+ pass->api_io.client->last_packet = cJSON_GNV(cJSON_GOI(json, "s"));
+ if (cJSON_GNV(cJSON_GOI(json, "op")) == DC_PING) {
pass->api_io.client->last_ping = 0;
dc_handle_ping(pass->api_io, NULL);
}
- if (cJSON_IsNumber((obj
- = cJSON_GetObjectItem2(json, "d", "heartbeat_interval")))) {
- pass->api_io.client->ping_interval
- = cJSON_GetNumberValue(obj)/1000-1;
+ if (cJSON_IN((obj = cJSON_GOI2(json, "d", "heartbeat_interval")))) {
+ pass->api_io.client->ping_interval = cJSON_GNV(obj)/1000-1;
pass->api_io.client->last_ping = 1;
}
#define DC_PARSEOBJ(w, object, also) if ((obj = object)) { \
struct dc_##w * w; \
- w = dc_parse_##w(NULL, obj); \
- w = dc_addr_##w(pass->api_io.program, \
+ if ((w = dc_parse_##w(NULL, obj))) \
+ w = dc_addr_##w(pass->api_io.program, \
DC_ISAE(pass->api_io.program->w##s), w, \
- DC_MAY_FREE | DC_REPLACE);\
+ DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);\
also \
}
- DC_PARSEOBJ(user, cJSON_GetObjectItem2(json, "d", "user"),
- if (!pass->api_io.client->user) pass->api_io.client->user = user;)
- DC_PARSEOBJ(user, cJSON_GetObjectItem3(json, "d", "member", "user"),)
- DC_PARSEOBJ(user, cJSON_GetObjectItem2(json, "d", "author"), )
- DC_PARSEOBJ(user, cJSON_GetObjectItem3(json, "d",
- "referenced_message", "author"), )
-#define DC_PARSEARR(what, arr) cJSON_ArrayForEach(obj, arr) { \
+ DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "user"),
+ if (!pass->api_io.client->user)
+ pass->api_io.client->user = user;
+ )
+ DC_PARSEOBJ(user, cJSON_GOI3(json, "d", "member", "user"),)
+ DC_PARSEOBJ(user, cJSON_GOI2(json, "d", "author"),)
+ DC_PARSEOBJ(user, cJSON_GOI3(json,
+ "d", "referenced_message", "author"),)
+#define DC_PARSEARR(what, arr, also) cJSON_AFE(obj, arr) { \
struct dc_##what * what = dc_parse_##what(NULL, obj); \
if (!what) \
continue; \
dc_addr_##what(pass->api_io.program, \
DC_ISAE(pass->api_io.program->what##s), \
- what, DC_MAY_FREE | DC_REPLACE); \
+ what, DC_MAY_FREE|DC_REPLACE|DC_INCOMPLETE);\
+ also \
}
- DC_PARSEARR(user, cJSON_GetObjectItem2(json, "d", "users"));
- DC_PARSEARR(user, cJSON_GetObjectItem2(json, "d", "mentions"));
- DC_PARSEARR(user, cJSON_GetObjectItem3(json, "d",
- "referenced_message", "mentions"));
-#define DC_PTYP(t) ((st = cJSON_GetStringValue(cJSON_GetObjectItem(json, "t"))) && !strcmp(st, t))
- if (DC_PTYP("MESSAGE_CREATE")) {
-
+ DC_PARSEARR(user, cJSON_GOI2(json, "d", "users"),)
+ DC_PARSEARR(user, cJSON_GOI2(json, "d", "mentions"),)
+ DC_PARSEARR(user, cJSON_GOI3(json,
+ "d", "referenced_message", "mentions"),)
+ cJSON_AFE(obj, cJSON_GOI2(json, "d", "private_channels")) {
+ if (/* !cJSON_GAS(cJSON_GOI(obj, "recipient_ids")) || */
+ /* commented. DMs with 0 members're shown /\ */ !cJSON_GSV(cJSON_GOI(obj, "id")) ||
+ !DC_CHANNEL_SUPPORTED(
+ /* cJSON is called many times here but I don't care */ cJSON_GNV(cJSON_GOI(obj, "type"))))
+ continue;
+ struct dc_channel * ch = dc_channel_init();
+ if ((st = cJSON_GSV(cJSON_GOI(obj, "name"))))
+ ch->name = strdup(st);
+ ch->type = cJSON_GNV(cJSON_GOI(obj, "type"));
+ ch->id = strtoull(cJSON_GSV(cJSON_GOI(obj, "id")), NULL, 10);
+ cJSON_AFE(obje, cJSON_GOI(obj, "recipient_ids")) {
+ struct dc_user * part = dc_user_init();
+ part->username = strdup("Private channel recipient");
+ part = dc_add_user(
+ DC_ISAE(pass->api_io.program->users),
+ /* no replace here. stored user can be better. */ part, DC_MAY_FREE);
+ DC_MR(ch->users); /* needn't dc_add here coz start */
+ ch->users[ch->users_length++] = part; /* empty ch. */
+ }
+ ch = dc_addr_channel(pass->api_io.program,
+ DC_ISAE(pass->api_io.program->channels), ch,
+ /* replace here. ours is better - fresher. */ DC_MAY_FREE | DC_REPLACE | DC_INCOMPLETE);
+ ch->guild = pass->api_io.client->guilds[0];
}
+ cJSON_AFE(obj, cJSON_GOI2(json, "d", "merged_members"))
+ cJSON_AFE(obje, cJSON_GOI(obj, "roles")) {
+ if (!(st = cJSON_GSV(obje)))
+ continue;
+ struct dc_role * role = dc_role_init();
+ role->id = strtoull(st, NULL, 10);
+ role = dc_add_role(
+ DC_ISAE(pass->api_io.program->roles),
+ role, DC_MAY_FREE);
+ struct dc_user * user = dc_user_init();
+ user->id = strtoull(cJSON_GSV(cJSON_GOI(
+ obj, "user_id")), NULL, 10);
+ user = dc_add_user(
+ DC_ISAE(pass->api_io.program->users),
+ /* again, no replace here, because we only have ID */ user, DC_MAY_FREE);
+ /* role may have users */ dc_add_user(DC_ISAE(role->users), user, DC_UNSET);
+ }
cJSON_Delete(json);
json = NULL;
serialized = dc_json(pass->json, NULL, 0);
@@ -292,7 +352,7 @@ void dc_api_i (struct dc_api_io i) { /* this function does not call attached fun
struct dc_lws_pass * pass;
assert(i.program);
if (i.program->lws_context && !(i.status & DC_FROM_LWS))
- lws_service(i.program->lws_context, 0); /* DO NOT CALL THIS FROM _cb! */
+ lws_service(i.program->lws_context, -1); /* DO NOT CALL THIS FROM _cb! */
switch (i.type) {
case DC_API_MESSAGE:
break;
@@ -440,7 +500,7 @@ struct dc_api_io dc_api_o (struct dc_api_io i /* for ->program */) {
return i;
}
if (i.program->lws_context)
- lws_service(i.program->lws_context, 0);
+ lws_service(i.program->lws_context, -1); /* with -1 this takes ~0 time */
for (size_t x = 0; x < i.program->clients_length; x++) {
if (i.program->clients[x]->status & DC_WS_ACTIVE && !i.program->clients[x]->pass
&& i.program->clients[x]->disconnect_time+DC_RECONNECT_DELAY<time(NULL)) {
diff --git a/src/h.c b/src/h.c
index 04fb7e9..ae6c8e0 100644
--- a/src/h.c
+++ b/src/h.c
@@ -10,7 +10,11 @@
} while (0) /* note that sizeof(name[0]) does not dereferencec name */
#define DC_MR(n) if (n##_sizeof <= n##_length) /* make room */ \
DC_BIGGER_ARRAY(n)
-#define DC_STRUCT_PREFIX void * data; /* some user data to be kind to api library users */
+#define DC_STRUCT_PREFIX void * data; /* some user data to be kind to api library users ... */
+#define DC_TRANSFER_PREFIX if (!n->data) { \
+ n->data = o->data; \
+ o->data = NULL; \
+ } /* ... and what happens with this data with _transfer_ */
#define DC_LWS_BUF 65535 /* 2^16 SMALL FUCKING HINT: ^ je XOR operator v C (in povsod drugje) */
#define DC_LWS_MAX_RX DC_LWS_BUF /* max bytes a websocket may handle in a single receive */
#define DC_LWS_MAX_FD 64 /* max file descriptors LWS will have open. if unset, LWS acquires all unused */
@@ -24,11 +28,11 @@
#endif
/* it's strongly recommended to calloc structs during initialization. */
enum dc_status { /* theese are flags and should be and-checked */
- DC_UNSET = 0, /* default value when enum is calloced */
- DC_INCOMPLETE = 1 << 0, /* struct SHALL NOT be used by the ui, it is yet to be filled by api */
+ DC_UNSET = 0, /* default value when enum is calloced */ /* \/ USEFUL FOR ->next!!! */
+ DC_INCOMPLETE = 1 << 0, /* _add_ w/ DC_REPLACE: new gets empty data from old w/ _transfer_ */
DC_OK = 1 << 1, /* success status and also ws established*/
DC_BAD_LOGIN = 1 << 2, /* login failed because of wrong credentials */
- DC_VERIFICATION_NEEDED = 1 << 3, /* login: check email, click link/reg: tough luck ur IP flagd */
+ DC_VERIFICATION_NEEDED = 1 << 3, /* login: check email, click link/reg: ur IP flagd */
DC_CAPTCHA_NEEDED = 1 << 4, /* must solve captcha, tough luck, not impl, use browser login */
DC_BAD_USERNAME = 1 << 5, /* provided username can't be registered */
DC_BAD_EMAIL = 1 << 6, /* provided email can't be registered */
@@ -41,17 +45,16 @@ enum dc_status { /* theese are flags and should be and-checked */
DC_ERROR = 1 << 13, /* unknown error, non implemented non expected response */
DC_NET_ERROR = 1 << 14, /* network failed or ws closed */
DC_LEJP_CONSTRUCTED = 1 << 15, /* json parser was constructed */
- DC_SET_PASS = 1 << 16, /* whether _CREATE _cb shall set client->passs. api sets on _WS create */
+ DC_SET_PASS = 1 << 16, /* if _CREATE _cb shall set client->passs. api sets on _WS create */
DC_IN_PROGRESS = 1 << 17, /* object is in parsing (by json_cb) */
DC_DESTROY_CB = 1 << 18, /* wether cb shall call api on DESTROY (used for http responses) */
- DC_MAY_FREE = 1 << 19, /* whether dc_*_add() shall free passed in pointer if found one in ISA */
- DC_WS_ACTIVE = 1 << 20, /* set at WSI_CREATE so api_o will reconnect WS if connection dropps */
+ DC_MAY_FREE = 1 << 19, /* if dc_*_add() shall free passed in pointer if found one in ISA */
+ DC_WS_ACTIVE = 1 << 20, /* set at WSI_CREATE so api_o'll reconnect WS if connection dropps */
DC_NO_WRITE = 1 << 21, /* signaling dc_ws_stack not to call lws_callback_on_writeable */
- DC_SET_WS_ACTIVE = 1 << 22, /* whether _CREATE _cb shall set client->status =| DC_WS_ACTIVE */
+ DC_SET_WS_ACTIVE = 1 << 22, /* if _CREATE _cb shall set client->status =| DC_WS_ACTIVE */
DC_REPLACE = 1 << 23, /* dc_add_x replace old with new on found, _free: only free members */
- DC_EXPLICIT_NULL = 1 << 24, /* MEMB_GC will NULL this member (PROGRES?aftr free) (& clear bit) */
- DC_EVERYONE = 1 << 25, /* role applies to all guild users */
- DC_USER_ERROR = 1 << 26, /* lib user made an error, passed NULL pointer to _o for example */
+ DC_EXPLICIT_NULL = 1 << 24, /* MEMB_GC'll NULL this memb (PROGRES?aftr free) (& clear bit) */
+ DC_USER_ERROR = 1 << 25, /* lib user made an error, passed NULL pointer to _o for example */
DC_INTERNAL = DC_FROM_LWS | DC_FROM_API, /* call originates from an internal function */
}; /* note: when checking status, first check for DC_OK, if it's set then disregard errors! */
#define DC_ADMIN (1 << 3) /* not all enum fields are implemented/understood */
@@ -60,14 +63,16 @@ enum dc_status { /* theese are flags and should be and-checked */
#define DC_MESSAGE_READ (1 << 16) /* na tistem vegova serverju sem lahko pošiljal ne pa bral sporočil */
#define DC_VOICE_LISTEN (1 << 20) /* ISO C enums are at most int-wide */
#define DC_VOICE_SPEAK (1 << 21)
-#define DC_ALL_PERMISSIONS (DC_ADMIN | DC_CHANNEL_VIEW | DC_MESSAGE_SEND | DC_MESSAGE_READ | DC_VOICE_LISTEN | DC_VOICE_SPEAK) /* admins get this@parsing, UI need not check admin separatly */
+#define DC_ALL_PERMISSIONS (DC_ADMIN | DC_CHANNEL_VIEW | DC_MESSAGE_SEND | DC_MESSAGE_READ \
+ | DC_VOICE_LISTEN | DC_VOICE_SPEAK) /* UI need not check ADMIN for WRITE 4example */
enum dc_channel_type { /* other types exist, but are not implemented/understood */
DC_GC, /* guild channel */
DC_DM, /* direct messages channel */
DC_VOICE, /* all enum fields here have values same as the values that the server sends */
DC_GROUP_DM
};
-#define DC_CHANNEL_SUPPORTED(x) (x == DC_GC || x == DC_DM || x == DC_VOICE || x == DC_GROUP_DM)
+#define DC_CHANNEL_SUPPORTED(x) ((x) == DC_GC || (x) == DC_DM || (x) == DC_VOICE || (x)==DC_GROUP_DM)
+char * dc_channel_type_str[] = { "DC_GC", "DC_DM", "DC_VOICE", "DC_GROUP_DM" };
enum dc_ws_packet { /* op numbers of websocket packets or json objects in other words */
DC_PING = 1,
DC_STRPKTOFF = 100, /* here follow string types (t) */
@@ -398,13 +403,13 @@ void dc_payload_free (struct dc_payload * s, enum dc_status t) {
free(s);
return;
}
-#define DC_ISASQ(shortname) DC_ISA(struct dc_##shortname, shortname##s) /* in struct array of structs quick */
-#define DC_ISAS_INIT(type/* w/o struct */, name) do { name##_sizeof = DC_ALLOC_CHUNK; /* structs ISA */ \
- name = calloc(name##_sizeof, sizeof(struct type *)); } while (0) /* prep arr, NO INIT membrs */
+#define DC_ISASQ(shortname) DC_ISA(struct dc_##shortname, shortname##s) /* ISA of structs quick */
+#define DC_ISAS_INIT(type/* w/o struct */, name) do { name##_sizeof = DC_ALLOC_CHUNK; \
+ name = calloc(name##_sizeof, sizeof(struct type *)); } while (0) /* prep arr, NO INIT mmbs */
#define DC_ISASIQ(shortname) DC_ISAS_INIT(dc_##shortname, s->shortname##s) /* ISAS init quick */
-#define DC_ISAF(shortname) for (size_t i = 0; i < s->shortname##s_length; i++) /* hmm, I used to fre */ \
- dc_##shortname##_free(s->shortname##s[i], DC_UNSET); /* till _sizeof, but now I fixd */ \
- free(s->shortname##s); /* to only free till _length. is this problematic in any ways? */
+#define DC_ISAF(shortname) for (size_t i = 0; i < s->shortname##s_length; i++) \
+ dc_##shortname##_free(s->shortname##s[i], DC_UNSET); \
+ free(s->shortname##s); /* only free till _length */
struct dc_client {
DC_STRUCT_PREFIX
char * authorization; /* yesfree - authorization header value */
@@ -416,7 +421,7 @@ struct dc_client {
unsigned long long int last_packet; /* last packet sequence number for ping */
time_t ping_interval;
time_t last_ping;
- time_t disconnect_time; /* set at disconnect, so that reconnection is delayed for some seconds */
+ time_t disconnect_time; /* set at disconnect, so that reconn is delayed for some secs */
DC_ISASQ(payload); /* yesfree - array of payloads we must send over ws */
DC_ISASQ(guild); /* yesfree array of pointers only - guilds of this user */
DC_ISASQ(user); /* yesfree array of pointers only - friends of this user */
@@ -426,13 +431,12 @@ struct dc_guild {
char * name; /* yesfree */
unsigned long long int id; /* 0 for virtual DMs guild */
struct dc_channel * channel; /* nofree - first channel */
- struct dc_role * role; /* nofree - first role. NOTE: role->id == guild->id => @everyone role */
+ struct dc_role * role; /* nofree - first role. NOTE: role->id==guild->id => @everyone */
enum dc_status status; /* /\ if NULL then assume all permissions - a DM guild */
#ifdef DC_UI_GTK
- GtkTreeIter iter; /* NOTE: only works when GtkTreeModel has a flag GTK_TREE_MODEL_ITERS_PERSIST; see paragraph 8 of description of file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */
- gboolean is_iter;
- /* GtkTreeRowReference * row; */ /* yesfree - indicating the row */ /* not used: IRC message: "00:51:29 @Company | also: Don't create too many tree row references, those things are slow" */
-#endif
+ GtkTreeIter iter; /* GtkTreeModel needs GTK_TREE_MODEL_ITERS_PERSIST for this to work */
+ gboolean is_iter; /* see parag 8 file:///usr/share/doc/libgtk-3-doc/gtk3/GtkTreeModel.html */
+#endif /* GtkTreeRowReference is not used, because it's slow (notice trimmed, see older commits) */
};
struct dc_guild * dc_guild_init () {
struct dc_guild * s = calloc(1, sizeof(*s));
@@ -489,8 +493,8 @@ struct dc_channel {
struct dc_guild * guild; /* nofree */
struct dc_channel * next; /* nofree - next channel (linked list of all channel of dc_guild) */
struct dc_message * message; /* nofree - first message (ordered by time) */
- DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */
enum dc_status status;
+ DC_ISASQ(permission); /* yesfree array and members - ch permissions for users/roles */
DC_ISASQ(user); /* yesfree array only - participants in DM channels */
#ifdef DC_UI_GTK
GtkTreeIter iter; /* see notes of struct dc_guild */
@@ -514,39 +518,31 @@ void dc_channel_free (struct dc_channel * s, enum dc_status t) {
if (!(t & DC_REPLACE))
free(s);
}
-struct dc_message {
+struct dc_message { /* mentions are not "cached" or parsed by API. UI should extract them. */
DC_STRUCT_PREFIX
char * message; /* yesfree */
struct dc_channel * channel; /* nofree */
struct dc_user * user; /* nofree */
- time_t time; /* obtained with DC_ID2TIME, edited timestamp is in ISO string format&not implemt */
+ time_t time; /* obtained with DC_ID2TIME, edited timestamp in ISO string format is N/I */
unsigned long long int id;
struct dc_message * next; /* next message (linked list of all messages of dc_channel) */
struct dc_message * reply; /* nofree - this message replies to another message or NULL */
enum dc_status status;
enum dc_message_type type;
- struct dc_client * client; /* nofree - set when sending message to the client that should send */
- DC_ISASQ(user); /* yesfree pointer array only - mentions */
- DC_ISASQ(role); /* yesfree pointer array only - mentions */
- DC_ISASQ(channel); /* yesfree pointer array only - mentions */
+ struct dc_client * client; /* nofree - set when sending message - which client should send */
};
struct dc_message * dc_message_init () {
struct dc_message * s = calloc(1, sizeof(*s));
- DC_ISASIQ(user);
- DC_ISASIQ(role);
- DC_ISASIQ(channel);
return s;
}
void dc_message_free (struct dc_message * s, enum dc_status t) {
if (!s)
return;
free(s->message);
- free(s->users);
- free(s->roles);
- free(s->channels);
if (!(t & DC_REPLACE))
free(s);
}
+#define DC_ROLE_EVERYONE(role) ((role)->guild && (role)->id == (role)->guild->id) /* triple eval!1 */
struct dc_role {
DC_STRUCT_PREFIX
char * name; /* yesfree */
@@ -554,8 +550,8 @@ struct dc_role {
unsigned long long int permissions; /* this are guild permission */
struct dc_guild * guild; /* nofree - owner of the role */
struct dc_role * next; /* nofree - next role (linked list of all roles of dc_guild) */
- DC_ISASQ(user); /* yesfree pointer array only - users with this role */
enum dc_status status;
+ DC_ISASQ(user); /* yesfree pointer array only - users with this role */
};
struct dc_role * dc_role_init () {
struct dc_role * s = calloc(1, sizeof(*s));
@@ -575,7 +571,6 @@ struct dc_user {
char * username; /* yesfree */
unsigned long long int id;
short int discriminator;
- struct dc_program * program; /* nofree - for dc_calculate_permissions */
enum dc_status status;
};
struct dc_user * dc_user_init () {
@@ -760,15 +755,16 @@ void dc_api_stack (struct dc_api_io);
if (us == u) \
return us; \
if (s & DC_REPLACE) { \
+ if (s & DC_INCOMPLETE) \
+ dc_transfer_##x(u /* this is then memmoved */, us); \
dc_##x##_free(us, s); \
memmove(us, u, sizeof(*us)); \
if (s & DC_MAY_FREE) \
free(u); /* we don't free members, they've been copied */ \
return us; \
} \
- if (s & DC_MAY_FREE) { \
+ if (s & DC_MAY_FREE) \
dc_##x##_free(u, DC_UNSET); \
- } \
return us; \
} \
if (*so <= *l) { \
@@ -797,53 +793,84 @@ void dc_api_stack (struct dc_api_io);
return u; \
} /* add with report */
#define DC_GEN_X(x, y) DC_FIND_X(x) DC_ADD_X(x) DC_ADDR_X(x, y)
-DC_GEN_X(user, USER)
-DC_GEN_X(channel, CHANNEL)
-DC_FIND_LL_X(channel)
-DC_GEN_X(guild, GUILD)
-DC_GEN_X(role, ROLE)
-DC_FIND_LL_X(role)
-DC_GEN_X(message, MESSAGE)
-DC_FIND_LL_X(message)
#define DC_ISAE(a) &(a), &(a##_sizeof), &(a##_length) /* ISA Expand */
#define DC_ISAN NULL, NULL, NULL /* ISA NULL */
-void dc_transfer_channel(struct dc_channel * n, struct dc_channel * o) { /* n is going to _REPLACE o */ \
- n->message = o->message; /* don't transfer perms because we get them in the new */ \
- n->next = o->next; /* new channel object */ \
+#define DC_TRANSFER_MEMBER(member) if (!n->member) { \
+ n->member = o->member; \
+ o->member = 0; \
+ }
+#define DC_TRANSFER_ISA(what) if (!n->what##s_length) { \
+ free(n->what##s); \
+ n->what##s = o->what##s; \
+ n->what##s_sizeof = o->what##s_sizeof; \
+ n->what##s_length = o->what##s_sizeof; \
+ o->what##s = NULL; \
+ o->what##s_sizeof = 0; \
+ o->what##s_length = 0; \
+ }
+void dc_transfer_channel (struct dc_channel * n, struct dc_channel * o) { /* n will _REPLACE o */ \
+ DC_TRANSFER_PREFIX
+ DC_TRANSFER_MEMBER(name) /* all _transfer_ functions assume old will be discarded */
+ DC_TRANSFER_MEMBER(topic) /* and passed to _free_ after use. */
+ DC_TRANSFER_MEMBER(id)
+ DC_TRANSFER_MEMBER(type)
+ DC_TRANSFER_MEMBER(guild)
+ DC_TRANSFER_MEMBER(next)
+ DC_TRANSFER_MEMBER(message)
+ DC_TRANSFER_ISA(permission)
+ DC_TRANSFER_ISA(user)
DC_IF_UI_GTK(
memmove(&n->iter, &o->iter, sizeof(GtkTreeIter));
n->is_iter = o->is_iter;
)
- if (!n->permissions_length) {
- free(n->permissions);
- n->permissions = o->permissions;
- n->permissions_sizeof = o->permissions_sizeof;
- n->permissions_length = o->permissions_length;
- o->permissions = NULL;
- o->permissions_sizeof = 0;
- o->permissions_length = 0;
- }
}
-#define DC_TRANSFER_GUILD(n, o) do { \
- DC_IF_UI_GTK( \
- memmove(&(n)->iter, &(o)->iter, sizeof(GtkTreeIter)); \
- (n)->is_iter = (o)->is_iter; \
- ) \
- } while (0)
+void dc_transfer_guild (struct dc_guild * n, struct dc_guild * o) {
+ DC_TRANSFER_PREFIX
+ DC_TRANSFER_MEMBER(name)
+ DC_TRANSFER_MEMBER(id)
+ DC_TRANSFER_MEMBER(channel);
+ DC_TRANSFER_MEMBER(role);
+ DC_TRANSFER_MEMBER(status);
+ DC_IF_UI_GTK(
+ memmove(&n->iter, &o->iter, sizeof(GtkTreeIter));
+ n->is_iter = o->is_iter;
+ )
+}
void dc_transfer_role (struct dc_role * n, struct dc_role * o) {
- n->next = o->next;
- if (!n->users_length) { /* if we didn't update users array in the new role object */
- free(n->users); /* thanks, clang, we have to free pointer array before overwriting it! */
- n->users = o->users;
- n->users_sizeof = o->users_sizeof;
- n->users_length = o->users_sizeof;
- o->users = NULL; /* so that free does not free array of pointers */
- o->users_sizeof = 0;
- o->users_length = 0;
- }
+ DC_TRANSFER_PREFIX
+ DC_TRANSFER_MEMBER(name)
+ DC_TRANSFER_MEMBER(id)
+ DC_TRANSFER_MEMBER(permissions)
+ DC_TRANSFER_MEMBER(guild)
+ DC_TRANSFER_MEMBER(next)
+ DC_TRANSFER_MEMBER(status)
+ DC_TRANSFER_ISA(user)
}
void dc_transfer_message (struct dc_message * n, struct dc_message * o) {
- n->next = o->next;
- if (!n->reply)
- n->reply = o->reply;
+ DC_TRANSFER_PREFIX
+ DC_TRANSFER_MEMBER(message)
+ DC_TRANSFER_MEMBER(channel)
+ DC_TRANSFER_MEMBER(user)
+ DC_TRANSFER_MEMBER(time)
+ DC_TRANSFER_MEMBER(id)
+ DC_TRANSFER_MEMBER(next)
+ DC_TRANSFER_MEMBER(reply)
+ DC_TRANSFER_MEMBER(status)
+ DC_TRANSFER_MEMBER(type)
+ DC_TRANSFER_MEMBER(client)
}
+void dc_transfer_user (struct dc_user * n, struct dc_user * o) {
+ DC_TRANSFER_PREFIX
+ DC_TRANSFER_MEMBER(username)
+ DC_TRANSFER_MEMBER(id)
+ DC_TRANSFER_MEMBER(discriminator)
+ DC_TRANSFER_MEMBER(status)
+}
+DC_GEN_X(user, USER)
+DC_GEN_X(channel, CHANNEL)
+DC_FIND_LL_X(channel)
+DC_GEN_X(guild, GUILD)
+DC_GEN_X(role, ROLE)
+DC_FIND_LL_X(role)
+DC_GEN_X(message, MESSAGE)
+DC_FIND_LL_X(message)
diff --git a/src/json.c b/src/json.c
index aae6f5d..bcf0d10 100644
--- a/src/json.c
+++ b/src/json.c
@@ -18,6 +18,7 @@ char * dc_json (struct dc_json * j, const char * in, int ln) { /* detects start/
if (j->ready > 0)
memmove(j->buf, j->buf+j->ready, strlen(j->buf+j->ready)+1);
j->buf[0] = j->backup;
+ j->ready = 0;
}
size_t bufstrlen = strlen(j->buf); /* could optimize and cache it into the struct */
if (ln == -1)
diff --git a/src/main.c b/src/main.c
index 7c3f3b9..1d9daaa 100644
--- a/src/main.c
+++ b/src/main.c
@@ -45,7 +45,17 @@ int main (int argc, char * argv[]) {
i = dc_api_o(i);
switch (i.type) {
case DC_API_USER:
- fprintf(stderr, "DC_API_USER: %s#%04d %llu\n", i.user->username, i.user->discriminator, i.user->id);
+ fprintf(stderr, "DC_API_USER: %s#%04d %llu\n", i.user->username,
+ i.user->discriminator, i.user->id);
+ break;
+ case DC_API_CHANNEL:
+ fprintf(stderr, "DC_API_CHANNEL participants: %u, type: %s\n",
+ i.channel->users_length,
+ dc_channel_type_str[i.channel->type]);
+ break;
+ case DC_API_LOGIN:
+ if (i.status & DC_CAPTCHA_NEEDED)
+ fprintf(stderr, "DC_CAPTCHA_NEEDED\n");
break;
default:
break;